.include "tn13adef.inc"
.include "usbmacro.inc"

.equ F_CPU = 12000000

; Common registers
.def GRB = R0
.def TMP = R16
.def TMP2 = R17
.def ARG0 = R18
.def ARG1 = R19
.def ZERO = R31

; Global variables
.def STATE = R20

; USB states
.def UINIT = R25
.def UIN = R26
.def UOUT = R27
.def J = R28
.def K = R29
.def SE0 = R31

; Pin assignment
.equ MASK_DP1 = (1 << PB0)
.equ MASK_DM1 = (1 << PB2)
.equ MASK_DP2 = (1 << PB1)
.equ MASK_DM2 = (1 << PB4)

; USB masks
.equ PORT1 = MASK_DP1 | MASK_DM1
.equ PORT2 = MASK_DP2 | MASK_DM2
.equ FS_J = MASK_DP1 | MASK_DP2
.equ FS_K = MASK_DM1 | MASK_DM2

; States
.equ ST_NONE = 0
.equ ST_INIT = 1
.equ ST_RESET0 = ST_INIT + 100
.equ ST_RESET1 = ST_RESET0 + 11
.equ ST_SET_ADDR0 = ST_RESET1 + 10
.equ ST_SET_ADDR1 = ST_SET_ADDR0 + 1
.equ ST_SET_CONF0 = ST_SET_ADDR1 + 3
.equ ST_SET_CONF1 = ST_SET_CONF0 + 1

; Two cycles delay
.macro nop2
  cbi PORTB, PB5
.endm

; Three cycles delay
.macro nop3
  lpm
.endm

; Eight cycles delay
.macro nop8
  rcall pnop8
.endm

; Wait for the next "ms" timer ticks, which occur at 1ms intervals
; Do not use when interrupts enabled
.macro wait_ms
  ldi ARG0, @0
  rcall pwait_ms
.endm

.macro case
  cpi STATE, @0
  brne @1
.endm

.macro casel
  cpi STATE, @0
  breq endm
  rjmp @1
endm:
.endm

.macro outi
  ldi TMP, @1
  out @0, TMP
.endm

.cseg

.org 0 ; RESET
  ; Stack setup
  outi SPL, low(RAMEND)
  ; Constants setup
  clr ZL
  clr ZH
  wdr
  rjmp start

; ~1kHz timer IRQ handler
.org OC0Aaddr
  usbd UOUT
; send SOF 1337
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs6 J, J, K, J, J, K
  nop2
  usbs2 J, K
  nop3
  usbs4 J, K, K, J
  nop2
  nop3
  usbs4 K, SE0, SE0, J
  usbd UIN
  usbs SE0
; ST_RESET0
  case ST_RESET0, reset0end
  or UOUT, UINIT
  mov ARG0, UINIT
  rcall check
  ldi TMP, ST_NONE
  cpse STATE, TMP
  or UIN, UINIT
reset0end:
; ST_RESET1
  case ST_RESET1, reset1end
  clr UIN
  rcall restorejk
  usbd UIN
  usbs SE0
reset1end:
; ST_SET_ADDR0
  casel ST_SET_ADDR0, setaddr0end
  rcall setjk
  usbd UINIT
; send SETUP
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs J
  nop2
  usbs10 K, K, J, K, J, K, J, K, J, K
  usbs10 J, K, J, K, J, K, K, J, K, J
  usbs3  SE0, SE0, J
; delay a little
  nop8
; send DATA0
  usbs7 K, J, K, J, K, J, K
  nop3
  usbs4 J, K, J, K
  nop2
  usbs10 J, K, J, K, J, K, J, K, K, J
  usbs10 J, K, J, K, J, K, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs4  K, J, K, J
  nop2
  usbs3 K, K, J
  nop2
  nop2
  usbs10 K, K, J, K, K, J, K, SE0, SE0, J
  usbd UIN
  usbs SE0
  rcall restorejk
setaddr0end:
; ST_SET_ADDR1
  case ST_SET_ADDR1, setaddr1end
  rcall setjk
  usbd UINIT
; send IN
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs4 J, K, K, J
  nop2
  usbs10 K, J, K, J, K, J, K, J, K, J 
  usbs10 K, J, K, K, J, K, J, SE0, SE0, J
; device will send DATA1 now.
  usbd UIN
  usbs SE0
; receive DATA1
  rcall recv_data1
  rcall restorejk
setaddr1end:
; ST_SET_CONF0
  casel ST_SET_CONF0, setconf0end
  rcall setjk
  usbd UINIT
; send SETUP
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs J
  nop2
  usbs10 K, K, J, K, K, J, K, J, K, J
  usbs7  K, J, K, J, K, K, J
  nop3
  usbs3 SE0, SE0, J
; delay a little
  nop8
; send DATA0
  usbs7 K, J, K, J, K, J, K
  nop3
  usbs4 J, K, J, K
  nop2
  usbs10 J, K, J, K, J, K, J, K, K, J
  usbs10 K, K, J, K, J, K, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs4  K, J, K, J
  nop3
  usbs10 K, J, J, K, J, J, K, K, J, K
  usbs6  K, J, K, SE0, SE0, J
  usbd UIN
  usbs SE0
  rcall restorejk
setconf0end:
; ST_SET_CONF1
  case ST_SET_CONF1, setconf1end
  rcall setjk
  usbd UINIT
; send IN
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs4 J, K, K, J
  nop2
  usbs10 K, K, J, K, J, K, J, K, J, K
  usbs4  J, K, K, J
  nop3
  usbs3 SE0, SE0, J
; device will send DATA1 now.
  usbd UIN
  usbs SE0
; receive DATA1
  rcall recv_data1
  rcall restorejk
  ldi STATE, ST_NONE
setconf1end:
  wdr
; ST_NONE
  case ST_NONE, noneend
  ldi ARG0, PORT1
  rcall check
  cpse ARG0, ZERO
  reti
  ldi ARG0, PORT2
  rcall check
  reti
noneend:
  inc STATE
  reti

; Entry point
start:
  ; WDT setup
  in TMP, WDTCR
  ori TMP, (1 << WDCE) | (1 << WDE)
  out WDTCR, TMP
  ldi TMP, (1 << WDE)
  out WDTCR, TMP
  ; Init variables
  clr UIN
  clr UOUT
  clr UINIT
  clr J
  clr K
  ldi STATE, ST_NONE
  ; Timer setup
  outi OCR0A, 46 ; ~1 ms
  outi TCCR0A, (1 << WGM01)
  outi TCCR0B, (1 << CS02)
  outi TIMSK0, (1 << OCIE0A)
  ; Init USB
  usbd UIN
  usbs SE0
  wait_ms 100
  sei
mainloop:
  rjmp mainloop

; See wait_ms
pwait_ms:
  outi TIFR0, (1 << OCF0A)
wait_ms_loop:
  in TMP, TIFR0
  andi TMP, (1 << OCF0A)
  breq wait_ms_loop
  wdr
  dec ARG0
  brne pwait_ms
pnop8:
  nop
  ret

; Check device presence at port ARG0
check:
  in TMP, PINB
  and TMP, ARG0
  ldi TMP2, FS_J
  and TMP2, ARG0
  cp TMP, TMP2
  breq present
  mov TMP, UOUT
  and TMP, ARG0
  breq checkend
  com ARG0
  and UOUT, ARG0
  and J, ARG0
  and K, ARG0
  ldi STATE, ST_NONE
  rjmp checkend
present:
  mov TMP, UOUT
  and TMP, ARG0
  brne checkend
  ldi STATE, ST_INIT
  mov UINIT, ARG0
  ser ARG0
  ret
checkend:  
  clr ARG0
  ret

setjk:
  and J, UINIT
  and K, UINIT
  ret

restorejk:
  ldi J, FS_J
  and J, UOUT
  ldi K, FS_K
  and K, UOUT
  ret

recv_data1:
; delay 43 cycles
  ldi TMP, 14
recv_data1_loop:
  dec TMP
  brne recv_data1_loop
  nop
  usbd UINIT
; send ACK
  usbs10 K, J, K, J, K, J, K, K, J, J
  usbs4  K, J, J, K
  nop2
  usbs3 SE0, SE0, J
  usbd UIN
  usbs SE0
  ret
